;; ====================================================
;;  Dirty but useful codes, 时间分析
;;

(defun org-list-clocks-current-item (check-n-days)
  "Extract clocked hours per day of current item"
  (org-with-point-at (bh/get-pom-from-agenda-restriction-or-point)
     (save-restriction
    (org-narrow-to-subtree)
    (let* ((next-day-secs (* 3600 25))
           (ts (format-time-string "%Y-%m-%d" (seconds-to-time (- (org-float-time (current-time)) (* check-n-days 3600 24)))))
           (te (org-time-delta-seconds ts next-day-secs))
           (res '())
           (dates '()))
      (while (< (org-float-time
                 (apply 'encode-time (org-parse-time-string ts)))
                (org-float-time (current-time)))
        (org-clock-sum ts te) ; slow
        (setq res (cons (/ org-clock-file-total-minutes 60.0) res)
              dates (cons (if (equal (get-day-time-string ts) "1")
                              "M"
                            (substring ts 8 10)) dates)
              ts te
              te (org-time-delta-seconds ts next-day-secs)))
      (list dates res)))))

(defun org-chart-clocks-current-item (day)
  "Request a Google Chart with the clocked time of the current item."
  (interactive "nDay to summary: ")
  (let* ((x (org-list-clocks-current-item day))
         (dates (reverse (car x)))
         (res (reverse (cadr x)))
         (active-days (length (remove-if (lambda (x) (= x 0)) res)))
         (sum-hours (reduce (lambda (a b) (+ a b)) res)))
    (browse-url (format "http://chart.apis.google.com/chart?chxl=0:|%s|1:|%s&chxr=0,0,%f|1,0,105&chxt=y,x&chbh=25&chs=600x400&cht=bvg&chco=006400&chds=0,%f&chd=t:%s&chg=0,%f&chma=|5,10&chtt=Clocked+Activity+(%d+days,+%d+hours)"
			(mapconcat (lambda (x) (number-to-string x) ) (number-sequence 1 (ceiling (maximum res))) "|")
                        (mapconcat (lambda (x) x) dates "|")
			(ceiling (maximum res))
			(ceiling (maximum res))
                        (mapconcat (lambda (x) (format "%s" x)) res  ",")
			(/ 100.0 (- (ceiling (maximum res)) 1))
                        active-days
                        sum-hours
                        (/ sum-hours active-days 8)))))

(defun maximum (list)
  (loop for x in list maximizing x))

(defun org-time-delta-seconds (time-string seconds)
  (format-time-string
   "%Y-%m-%d"
   (seconds-to-time
    (+ (org-float-time (apply 'encode-time
                              (org-parse-time-string time-string)))
       seconds))))

(defun get-day-time-string (time-string)
  (format-time-string
   "%u"
   (seconds-to-time
    (org-float-time
     (apply 'encode-time (org-parse-time-string time-string))))))

;; 从 agenda 读取日期的两个函数，一个返回string，一个返回 (high low)值
(defun xwp/org-agenda-read-time-string ()
  (interactive)
  (when (eq major-mode 'org-agenda-mode)
      (beginning-of-buffer)
    (forward-line)
    (buffer-substring-no-properties (line-beginning-position) (line-end-position))))

(defun xwp/agenda-get-myday ()
  (interactive)
  (let* ( 
	 (nowday (current-time))
	 (daydif nil)
	 )
    (when (eq major-mode 'org-agenda-mode)
      (beginning-of-buffer)
      (forward-line)
      (when (org-get-at-bol 'day)
	(setq daydif (- (org-get-at-bol 'day) (time-to-days (current-time))))
	(setq nowday (time-add (current-time) (days-to-time daydif))))
    ;; (print (format-time-string "%Y-%m-%d" nowday))
      )
    (identity nowday)
))
;; 每天每个agenda org文件所耗费的时间
(defun xwp/org-agenda-file-time-day ()
  (interactive)
  (let* (
	 ;; (files (org-agenda-files))
	 (files (cons "~/emacs/org/sleep.org" (org-agenda-files)))
	 (file-list '())
	 (time-list '())
	 (timestring nil)
	 (daystart nil)
	 (para '(:maxlevel 1 :block nil))
	      )
    (if (eq major-mode 'org-agenda-mode)
	(setq timestring (parse-time-string (xwp/org-agenda-read-time-string)))
      (setq timestring (parse-time-string (format-time-string "%Y-%m-%d"))))
    (setq daystart (format-time-string "%Y-%m-%d" 
					 (encode-time 0 0 0 (nth 3 timestring) (nth 4 timestring) (nth 5 timestring) 0 0 0 )))
    (plist-put para :block (make-symbol daystart))
    (org-agenda-prepare-buffers files)
    (while (setq file (pop files))
      (with-current-buffer (find-buffer-visiting file)
      (save-excursion
	(save-restriction
	  ;; (when (> (nth 1 (org-clock-get-table-data file '(:maxlevel 1 :block today))) 0)
	       (setq file-list (cons (file-name-sans-extension 
				      (file-name-nondirectory
				       (nth 0 (org-clock-get-table-data file para))))  file-list))
	       (setq time-list (cons (nth 1 (org-clock-get-table-data file para)) time-list))
	       ))))
(list file-list time-list)))
;; 将每天的时间绘制成饼图
(defun xwp/chart-org-agenda-day ()
  (interactive)
  (let* ((x (xwp/org-agenda-file-time-day))
	 (file (car x))
	 (time (cadr x))
	 (timesum (apply '+ time))
	 (chdt '())
	 (chlt '())
	 (i 0)
	 (timestring nil)
	 (daystart nil)
	 (chlstring nil)
	 (chdstring nil)
	 )
  (if (eq major-mode 'org-agenda-mode)
	(setq timestring (parse-time-string (xwp/org-agenda-read-time-string)))
      (setq timestring (parse-time-string (format-time-string "%Y-%m-%d"))))
    (setq daystart (format-time-string "%Y-%m-%d" 
					 (encode-time 0 0 0 (nth 3 timestring) (nth 4 timestring) (nth 5 timestring) 0 0 0 )))
    (while (< i (length file))
      (if (< (nth i time) 10)
	  (setq chlstring "" chdt (cons (nth i file) chdt))
	  (setq chlstring (format "%s:%s" (nth i file) (format "%dh%02dm" (/ (nth i time) 60) (mod (nth i time) 60))) chdstring ""))
      (setq chlt (cons chlstring chlt) )
      (setq i (+ i 1))
      )
    (setq chl (reverse chlt) chd (reverse chdt))
    (browse-url (format "http://chart.apis.google.com/chart?chs=800x350&cht=p3&chd=t:%s&chl=%s&chdl=%s&chtt=Day:+%s++++Time:+%s&chts=0000FF,20&chco=%s&chdls=0000ff,14"
			(mapconcat (lambda (x) (format "%s" (/ x (float timesum)))) time ",")
			(mapconcat (lambda (x) x) chl "|")
			(mapconcat (lambda (x) x) chd "|")
			daystart
			;; timesum
			(format "%dh%02dm" (/ timesum 60) (mod timesum 60))
			(mapconcat (lambda (x) x) xwp/dist-color-list ",")			
))))



;; 每周每个文件所耗费的时间
(defun xwp/org-agenda-file-time-week ()
  (interactive)
  (let* (
	 (files (org-agenda-files))
	 (file-list '())
	 (time-list '())
	 (para '(:maxlevel 1 :tstart nil :tend nil))
	 (dayofweek 0)
	 (timestring nil)
	 (timeencode nil)
	 (timesec nil)
	 (timemon nil)
	 (timefri nil)
	      )
    (if (eq major-mode 'org-agenda-mode)
	(setq timestring (parse-time-string (xwp/org-agenda-read-time-string)))
      (setq timestring (parse-time-string (format-time-string "%Y-%m-%d"))))
    (setq timeencode (encode-time 0 0 0 (nth 3 timestring) (nth 4 timestring) (nth 5 timestring) 0 0 0 ))
    (setq dayofweek (string-to-number (format-time-string "%w" timeencode)))
    (when (eq dayofweek 0)
      (setq dayofweek 7))
    (setq timesec (+ (* (nth 0 timeencode) 65536) (nth 1 timeencode)))
    (setq timemon (seconds-to-time (- timesec (* (- dayofweek 1) (* 24 3600)))))
    (setq timefri (seconds-to-time (+ timesec (* (- 7 dayofweek) (* 24 3600)))))
    (plist-put para :tstart (format-time-string "%Y-%m-%d" timemon))
    (plist-put para :tend (format-time-string "%Y-%m-%d" timefri))
    (org-agenda-prepare-buffers files)
    (while (setq file (pop files))
      (with-current-buffer (find-buffer-visiting file)
      (save-excursion
	(save-restriction
	  ;; (when (> (nth 1 (org-clock-get-table-data file '(:maxlevel 1 :block today))) 0)
	       (setq file-list (cons (file-name-sans-extension 
				      (file-name-nondirectory
				       (nth 0 (org-clock-get-table-data file para))))  file-list))
	       (setq time-list (cons (nth 1 (org-clock-get-table-data file para)) time-list))))))
    (list file-list time-list timemon timefri)))
;; 将时间绘制成饼图
(defun xwp/chart-org-agenda-week ()
  (interactive)
  (let* ((x (xwp/org-agenda-file-time-week))
	 (file (car x))
	 (time (nth 1 x))
	 (timemon (nth 2 x))
	 (timefri (nth 3 x))
	 (timesum (apply '+ time))
	 (chdt '())
	 (chlt '())
	 (i 0)
	 (timestring nil)
	 (daystart nil)
	 (chlstring nil)
	 (chdstring nil)
	 )
    (if (eq major-mode 'org-agenda-mode)
	(setq timestring (parse-time-string (xwp/org-agenda-read-time-string)))
      (setq timestring (parse-time-string (format-time-string "%Y-%m-%d"))))
    (setq daystart (format-time-string "%Y-%m-%d" 
					 (encode-time 0 0 0 (nth 3 timestring) (nth 4 timestring) (nth 5 timestring) 0 0 0 )))
    (while (< i (length file))
      (if (< (nth i time) 10)
	  (setq chlstring "" chdt (cons (nth i file) chdt))
	  (setq chlstring (format "%s:%s" (nth i file) (format "%dh%02dm" (/ (nth i time) 60) (mod (nth i time) 60))) chdstring ""))
      (setq chlt (cons chlstring chlt) )
      (setq i (+ i 1))
      )
    (setq chl (reverse chlt) chd (reverse chdt))
    (browse-url (format "http://chart.apis.google.com/chart?chs=800x350&cht=p3&chd=t:%s&chl=%s&chdl=%s&chtt=Week:+%s(%s~%s)++++Time:+%s&chts=0000FF,15&chco=%s&chdls=0000ff,14"
			(mapconcat (lambda (x) (format "%s" (* 100 (/ x (float timesum))))) time ",")
			(mapconcat (lambda (x) x) chl "|")
			(mapconcat (lambda (x) x) chd "|")
			(format-time-string "%W" timemon)
			(format-time-string "%Y-%m-%d" timemon)
			(format-time-string "%Y-%m-%d" timefri)
			;; timesum
			(format "%dh%02dm" (/ timesum 60) (mod timesum 60))
			(mapconcat (lambda (x) x) xwp/dist-color-list ",")
))))

(defun xwp/org-agenda-item-day-time ()
  (interactive)
  (let* (
	 (pairlist xwp/org-items-list-day)
	 (nowday (xwp/agenda-get-myday))
	 (itemtime '())
	 (item '())
	 (tstart (format-time-string "%Y-%m-%d" nowday))
	 (tend (format-time-string "%Y-%m-%d" (time-add nowday (days-to-time 1))))
	 (idmarker nil)
	 (id nil)
	 (name nil)
	 (pair nil)
	 )
	(while (setq pair (pop pairlist))
	  (setq id (cadr pair) name (car pair))
	  (setq idmarker (org-id-find id 'marker))
	  (org-with-point-at idmarker
	    (save-excursion
	      (save-restriction
		(widen)
		(org-with-point-at idmarker
		  (org-narrow-to-subtree)
		  (setq singletime (org-clock-sum tstart tend))
		  (if (< singletime 15)
		      (setq singletime 0)
		    (setq name (format "%s(%dh%02dm)" name (/ singletime 60) (mod singletime 60)))
		    )
		  (setq item (cons (list name singletime) item))
		  )))))
	(sort item #'(lambda (a b) (> (cadr a) (cadr b))))
))

(defun xwp/org-agenda-item-week-time ()
  (interactive)
  (let* (
	 (pairlist xwp/org-items-list-week)
	 (nowday (xwp/agenda-get-myday))
	 (weekday (string-to-number (format-time-string "%w" nowday)))
	 (itemtime '())
	 (item '())
	 (monday nil)
	 (sunday nil)
	 (idmarker nil)
	 (pair nil)
	 (id nil)
	 (name nil)
	 )
    (when (eq weekday 0)
      (setq weekday 7))
    (setq monday (format-time-string "%Y-%m-%d" (time-add nowday (days-to-time (- 1 weekday))))
	  sunday (format-time-string "%Y-%m-%d" (time-add nowday (days-to-time (- 7 weekday)))))
	(while (setq pair (pop pairlist))
	  (setq id (cadr pair) name (car pair))
	  (setq idmarker (org-id-find id 'marker))
	  (org-with-point-at idmarker
	    (save-excursion
	      (save-restriction
		(widen)
		(org-with-point-at idmarker
		  (org-narrow-to-subtree)
		  (setq singletime (org-clock-sum monday sunday))
		  (if (< singletime 15)
		      (setq singletime 0)
		    (setq name (format "%s(%dh%02dm)" name (/ singletime 60) (mod singletime 60)))
		    )
		  (setq item (cons (list name singletime) item))
		  )))))
	(sort item #'(lambda (a b) (> (cadr a) (cadr b))))
))

(defun xwp/chart-org-agenda-item-day ()
  (interactive)
  (let* ((item (xwp/org-agenda-item-day-time))
	 (i 0)
	 (timesum 0)
	 (zerolist '())
	 )
    (while (< i (length item))
      (when (= (cadr (nth i item)) 0) (setq zerolist (cons (car (nth i item)) zerolist)))
      (setq timesum (+ timesum (cadr (nth i item))) i (+ i 1)))
    ;; (print item)
    (browse-url (format "http://chart.apis.google.com/chart?chs=800x370&cht=p&chd=t:%s&chl=%s&chtt=Day:+%s++++Time:+%s&chts=0000FF,20&chco=%s&chdl=%s&chdls=0000ff,14"
			(mapconcat (lambda (x) (format "%s" (/ (cadr x) (float timesum)))) item ",")
			(mapconcat (lambda (x) (if (= (cadr x) 0) (format "") (car x))) item "|")
			;; (mapconcat (lambda (x) (car x)) item "|")
			(format-time-string "%Y-%m-%d" (xwp/agenda-get-myday))
			(format "%dh%02dm" (/ timesum 60) (mod timesum 60))
			(mapconcat (lambda (x) x) xwp/dist-color-list ",")
			(mapconcat (lambda (x) x) zerolist "|")
			;; (mapconcat (lambda (x) (if (= (cadr x) 0) (car x) (nil) )) item "|")
))))

(defun xwp/chart-org-agenda-item-week ()
  (interactive)
  (let* (
	 (item (xwp/org-agenda-item-week-time))
	 (i 0)
	 (timesum 0)
	 (zerolist '())
	 )
    (while (< i (length item))
      (when (= (cadr (nth i item)) 0) (setq zerolist (cons (car (nth i item)) zerolist)))
      (setq timesum (+ timesum (cadr (nth i item))) i (+ i 1)))
    (browse-url (format "http://chart.apis.google.com/chart?chs=800x370&cht=p&chd=t:%s&chl=%s&chtt=Week+%s++++Time:+%s&chts=0000FF,20&chco=%s&chdl=%s&chdls=0000ff,14"
			(mapconcat (lambda (x) (format "%s" (/ (cadr x) (float timesum)))) item ",")
			(mapconcat (lambda (x) (if (= (cadr x) 0) (format "") (car x))) item "|")
			;; (mapconcat (lambda (x) (car x)) item "|")
			(format-time-string "%W" (xwp/agenda-get-myday))
			(format "%dh%02dm" (/ timesum 60) (mod timesum 60))
			(mapconcat (lambda (x) x) xwp/dist-color-list ",")
			(mapconcat (lambda (x) x) zerolist "|")
))))


(defun xwp/chart-choose ()
  (interactive)
  (let ((defaults '(:name "columnview" :hlines 1))
	 (id (org-icompleting-read
	    "Chart Type: "
	    (append '(("day") ("week") ("file") ("totalweek") )))))
    ;; (if (equal id "current") (org-chart-clocks-current-item))
    (if (equal id "day") (xwp/chart-org-agenda-item-day))
    (if (equal id "week") (xwp/chart-org-agenda-item-week))
    (if (equal id "file") (xwp/chart-org-agenda-day))
    (if (equal id "totalweek") (xwp/chart-org-agenda-week))
))

(add-hook 'org-agenda-mode-hook
          '(lambda ()
	     (org-defkey org-agenda-mode-map (kbd "<f11>") 'xwp/chart-choose)
	     )
          'append)
(setq xwp/dist-color-list '("204A87" "A40000" "797979" "222222" "b8860b" "BDB76B" "00BFFF" "00008B" "FFFF00" "EDD400" "B8E186" "EF2929" "8B008B" "C51B7D" "32888D" "C2A5CF" "F46D43" "8C410A" "92C5DE" "5E4FA2" "9E0142" "FDAE61" ))
(provide 'org-time-analysis)
